/***********************************************************************************/
/* Code license:  GNU General Public License v3                                    */
/* Main page:     https://code.google.com/p/library-cross-platform/                */
/* Issue tracker: https://code.google.com/p/library-cross-platform/issues/list     */
/***********************************************************************************/

#ifndef _MATH_X_H_1911111839_
#define _MATH_X_H_1911111839_

#include <stdlib.h>
#include <math.h>

#ifdef __cplusplus
extern "C" {
#endif

/*************************************************************************
DESCRIPTION:
Returns the inverse hyperbolic tangent of a number.
Number must be between -1 and 1 (excluding -1 and 1).
The inverse hyperbolic tangent is the value whose
hyperbolic tangent is number, so ATANH(TANH(number)) equals number.

IN:
Number is any real number between 1 and -1.

*************************************************************************/
double _atanh(const double & v);


/*************************************************************************
DESCRIPTION:
Transform double value to 2 int digits: rem and quot.
note: the modf(...) can be used as alternative.
123.001234 -> result: 123 and 1234. (00 will be skiped)
IN:
 double value
 number of digits in the quot

RETURN:
 div_t

*************************************************************************/
div_t double_transform(const double &v, int digits);


#ifdef __cplusplus
}
#endif


namespace math_round
{

template <long num, size_t n, size_t y = 1>
struct pow
{
    enum { value = pow<num * num, (n >> 1), n & 1 ? num * y : y>::value };
};
template <long num, size_t y>
struct pow<num, 0, y>
{
    enum { value = y };
};


/*************************************************************************
DESCRIPTION:
  Round a number
IN:
 1st template parameter is precision
 2nd template parameter is digit type (float, double, long double)

 ex:
   double dv = -11.123456789;
   double r = tmp::round<4, double>(dv);
   result: -11.123500

RETURN:
 2nd template type
*************************************************************************/
#if __cplusplus > 199711L
template <size_t Precision, typename typeFloat = double>
#else
template <size_t Precision, typename typeFloat>
#endif
inline typeFloat round(typeFloat x)
{
    enum
    {
        PrecMul = math_round::pow<10, Precision>::value
    };
    return (x > 0. ? floor(x * PrecMul + 0.5) : ceil(x * PrecMul - 0.5)) / PrecMul;
}



/*************************************************************************
DESCRIPTION:
  Round a number
IN:
 - template parameter is digit type (float, double, long double)
 1st - input value
 2nd - precision

 ex:
   double dv = -11.123456789;
   double r1 = tmp::round<double>(dv,4);
   result: -11.123500

RETURN:
 typeFloat
*************************************************************************/
#if __cplusplus > 199711L
template <typename typeFloat = double>
#else
template <typename typeFloat>
#endif
typeFloat round (typeFloat x, int precision)
{
    long mul = 1;

    for (int i = 0; i < precision; i++)
        mul *= 10;
    if (x > 0)
        return floor(x * mul + .5) / mul;
    else
        return ceil(x * mul - .5) / mul;
}

} //math_round

#endif //_MATH_X_H_1911111839_
